﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Timtopia.PowerGraphBeamSearch;

namespace Timtopia.DGMLPosterView
{
    using Microsoft.VisualStudio.GraphModel;
    using Tree = Timtopia.DGMLPosterView.PowerGraphDecomposition.TreeNode<int?>;
    public class GraphMLWriter
    {
        public static void WriteFile(Graph g, string filename)
        {
            using (var outfile = new StreamWriter(filename))
            {
                WriteHeader(outfile);
                var moduleLookup = new Dictionary<int, Module>();
                var moduleIDs = new Dictionary<Module, string>();
                foreach (var v in g.OrphanNodes)
                {
                    if (v.IsGroup)
                    {
                        WriteGroup(outfile, g.FindGroup(v));
                    }
                    else
                    {
                        WriteNode(outfile, v);
                    }
                }
                WriteGraphMLEdges(g, outfile);
                outfile.WriteLine("</graphml>");
            }
        }

        private static void WriteNode(StreamWriter outfile, GraphNode v)
        {
            outfile.WriteLine(@"<node id=""" + v.Id + @""">");
            WriteGraphMLNodeLabel(outfile, v.Id.ToString());
            outfile.WriteLine(@"</node>");
        }

        private static void WriteGraphMLEdges(Graph g, StreamWriter outfile)
        {
            int i = 0;
            foreach (var e in g.Links.Where(e=>!e.HasCategory(GraphCommonSchema.Contains)))
            {
                outfile.WriteLine(@"<edge id=""e" + i++ +@""" source=""" + e.Source.Id + @""" target=""" + e.Target.Id + @"""/>");
            }
        }

        private static void WriteGroup(StreamWriter outfile, GraphGroup graphGroup)
        {
            outfile.WriteLine(@"<node id=""" + graphGroup.Id + @""">");
            outfile.WriteLine(@"<graph id=""m" + graphGroup.Id + @""" edgedefault=""directed"">");
            foreach (var v in graphGroup.ChildNodes)
            {
                WriteNode(outfile, v);
            }
            foreach (var v in graphGroup.ChildGroups)
            {
                WriteGroup(outfile, v);
            }
            outfile.WriteLine(@"</graph>");
            outfile.WriteLine(@"</node>");
        }
        public static void WriteFile(Modules ms, string filename, IList<Tuple<string, string>> labelMap = null)
        {
            if (labelMap == null)
            {
                labelMap = new List<Tuple<string, string>>();
                for (int i = 0; i < ms.N; ++i)
                {
                    labelMap.Add(Tuple.Create(i.ToString(), i.ToString()));
                }
            }
            using (var outfile = new StreamWriter(filename))
            {
                WriteHeader(outfile);
                var moduleLookup = new Dictionary<int, Module>();
                var moduleIDs = new Dictionary<Module, string>();
                WriteGraphMLNodes(ms, outfile, ms.Tree, moduleLookup, moduleIDs, labelMap);
                WriteGraphMLEdges(ms, outfile, ms.Tree, moduleIDs);
                outfile.WriteLine("</graphml>");
            }
        }

        private static void WriteHeader(StreamWriter outfile)
        {
            outfile.Write(@"<?xml version=""1.0"" encoding=""UTF-8""?>
<graphml xmlns=""http://graphml.graphdrawing.org/xmlns""
xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" 
xmlns:y=""http://www.yworks.com/xml/graphml"" 
xmlns:yed=""http://www.yworks.com/xml/yed/3"" 
xsi:schemaLocation=""http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd"">
  <key for=""node"" id=""d0"" yfiles.type=""nodegraphics""/>
");
        }
        private static void WriteGraphMLEdges(Modules ms, StreamWriter s, Tree t, Dictionary<Module, string> moduleIDs)
        {
            Module m = ms.GetModule(t);
            if (t.Children != null)
            {
                foreach (var c in t.Children)
                {
                    WriteGraphMLEdges(ms, s, c, moduleIDs);
                }
            }
            if (m != null)
            {
                string mid = moduleIDs[m];
                foreach (var e in m.Outgoing)
                {
                    string target = moduleIDs[e];
                    s.WriteLine(@"<edge id=""e" + mid + target + @""" source=""" + mid + @""" target=""" + target + @"""/>");
                }
            }
        }

        private static void WriteGraphMLNodes(Modules ms, StreamWriter s, Tree t, Dictionary<int, Module> moduleLookup, Dictionary<Module, string> moduleIDs, IList<Tuple<string, string>> labelMap)
        {
            Module m = ms.GetModule(t);
            string mid;
            if (t.Children != null)
            {
                if (m != null)
                {
                    moduleLookup[moduleLookup.Count] = m;
                    mid = "nm" + moduleLookup.Count;
                    moduleIDs[m] = mid;
                    s.WriteLine(@"<node id=""" + mid + @""">");
                }
                s.WriteLine(@"<graph id=""m" + moduleLookup.Count + @""" edgedefault=""directed"">");
                foreach (var c in t.Children)
                {
                    WriteGraphMLNodes(ms, s, c, moduleLookup, moduleIDs, labelMap);
                }
                s.WriteLine(@"</graph>");
            }
            else
            {
                m = ms.GetModule(t);
                mid = "n" + t.Leaf;
                moduleIDs[m] = mid;
                s.WriteLine(@"<node id=""" + mid + @""">");
                WriteGraphMLNodeLabel(s, labelMap[t.Leaf.Value].Item1);
            }
            if (m != null)
            {
                s.WriteLine(@"</node>");
            }
        }

        private static void WriteGraphMLNodeLabel(StreamWriter s, string label)
        {
            s.WriteLine(@"<data key=""d0""><y:ShapeNode><y:NodeLabel>" + label + "</y:NodeLabel></y:ShapeNode></data>");
        }
    }
}
